package com.seng.resourcetycoon.gui.util;

import com.seng.resourcetycoon.core.enums.DifficultyLevel;
import com.seng.resourcetycoon.core.enums.ResourceType;
import com.seng.resourcetycoon.event.OnGameWinListener;
import com.seng.resourcetycoon.gui.models.LoadBearer;
import com.seng.resourcetycoon.gui.views.LoadBearerItemView;
import com.seng.resourcetycoon.manager.GameEnvironment;
import com.seng.resourcetycoon.services.impl.LoadBearerServiceImpl;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;

public class LoadBearerServiceImplTest1 {

    @Mock
    private LoadBearer loadBearerMock1;
    @Mock
    private LoadBearer loadBearerMock2;
    @Mock
    private LoadBearerItemView loadBearerItemViewMock;

    @Mock
    private LoadBearerServiceImpl cartService;
    private List<LoadBearer> loadBearers;
    private List<OnGameWinListener> gameWinListeners;

    @BeforeEach
    void setUp() {
        try (MockedStatic<GameEnvironment> gameEnvironment = Mockito.mockStatic(GameEnvironment.class)) {
            when(GameEnvironment.getInstance()).thenReturn(Mockito.mock(GameEnvironment.class));

            when(GameEnvironment.getInstance().getDifficultyLevel()).thenReturn(DifficultyLevel.EASY);

            cartService = Mockito.spy(LoadBearerServiceImpl.class);
            MockitoAnnotations.openMocks(this);
            // Assuming cartItemViews is a List<CartItemView> field in CartServiceImpl

            List<LoadBearerItemView> loadBearerItemViews = new ArrayList<>();
            loadBearerItemViews.add(loadBearerItemViewMock);
            Field cartsViewsField = null;
            try {
                cartsViewsField = LoadBearerServiceImpl.class.getDeclaredField("loadBearerItemViews");
                cartsViewsField.setAccessible(true);
                cartsViewsField.set(cartService, loadBearerItemViews);
            } catch (NoSuchFieldException | IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }
    }

    @Test
    void testDistributeResource() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        // Arrange
        ResourceType resourceType = ResourceType.WATER;
        int delta = 10;
        int cart1Capacity = 5;
        int cart2Capacity = 5;
        int totalCapacity = cart1Capacity + cart2Capacity;

        when(loadBearerMock1.tryAddToCart(eq(resourceType), anyInt())).thenAnswer(invocation -> {
            int quantity = invocation.getArgument(1);
            // Assuming getCurrentCapacityForResource returns current capacity for the cart
            int currentCapacity = 0; // You should set this based on your implementation
            int spaceAvailable = cart1Capacity - currentCapacity;
            return Math.min(spaceAvailable, quantity);
        });

        when(loadBearerMock2.tryAddToCart(eq(resourceType), anyInt())).thenAnswer(invocation -> {
            int quantity = invocation.getArgument(1);
            // Assuming getCurrentCapacityForResource returns current capacity for the cart
            int currentCapacity = 0; // You should set this based on your implementation
            int spaceAvailable = cart2Capacity - currentCapacity;
            return Math.min(spaceAvailable, quantity);
        });

        List<LoadBearer> unfilledLoadBearers = Arrays.asList(loadBearerMock1, loadBearerMock2);

        // You need to set this to return your unfilledCarts list
        when(cartService.getUnfilledCartsForResourceType(resourceType)).thenReturn(unfilledLoadBearers);

        // Act
        // Use reflection to invoke the private method randomCreateCart
        Method method = LoadBearerServiceImpl.class.getDeclaredMethod("distributeResource", ResourceType.class, int.class);
        method.setAccessible(true);
        method.invoke(cartService, resourceType, delta);

        // Assert
        verify(loadBearerMock1, times(1)).tryAddToCart(eq(resourceType), eq(delta));
        verify(loadBearerMock2, times(1)).tryAddToCart(eq(resourceType), eq(delta));
        verify(loadBearerItemViewMock, times(1)).refreshLoadedCapacities();
    }

    @Test
    void testCheckAndNotifyGameWin_AllRequirementsMet_ShouldNotifyListeners() throws Exception {
        mockCartAndListener();

        when(loadBearers.get(0).allRequirementsMet()).thenReturn(true);
        when(loadBearers.get(1).allRequirementsMet()).thenReturn(true);

        Method checkAndNotifyGameWin = LoadBearerServiceImpl.class.getDeclaredMethod("checkAndNotifyGameWin");
        checkAndNotifyGameWin.setAccessible(true);
        boolean result = (boolean) checkAndNotifyGameWin.invoke(cartService);

        Assertions.assertTrue(result);
        for (OnGameWinListener listener : gameWinListeners) {
            verify(listener, times(1)).onGameWin();
        }
    }

    private void mockCartAndListener() throws NoSuchFieldException, IllegalAccessException {
        loadBearers = Arrays.asList(mock(LoadBearer.class), mock(LoadBearer.class));
        gameWinListeners = Arrays.asList(mock(OnGameWinListener.class), mock(OnGameWinListener.class));

        Field cartsField = LoadBearerServiceImpl.class.getDeclaredField("loadBearers");
        cartsField.setAccessible(true);
        cartsField.set(cartService, loadBearers);

        Field gameWinListenersField = LoadBearerServiceImpl.class.getDeclaredField("gameWinListeners");
        gameWinListenersField.setAccessible(true);
        gameWinListenersField.set(cartService, gameWinListeners);
    }

    @Test
    void testCheckAndNotifyGameWin_NotAllRequirementsMet_ShouldNotNotifyListeners() throws Exception {
        mockCartAndListener();
        // 设置至少一个Cart的allRequirementsMet返回false
        when(loadBearers.get(0).allRequirementsMet()).thenReturn(true);
        when(loadBearers.get(1).allRequirementsMet()).thenReturn(false);

        Method checkAndNotifyGameWin = LoadBearerServiceImpl.class.getDeclaredMethod("checkAndNotifyGameWin");
        checkAndNotifyGameWin.setAccessible(true);
        boolean result = (boolean) checkAndNotifyGameWin.invoke(cartService);

        Assertions.assertFalse(result);
        for (OnGameWinListener listener : gameWinListeners) {
            verify(listener, never()).onGameWin();
        }
    }

    @Test
    public void testAddResource() {
        ResourceType mockResourceType = ResourceType.FOOD;

        Map<ResourceType,Integer> requiredResources = new HashMap<>();
        requiredResources.put(mockResourceType,150);
        LoadBearer loadBearer = new LoadBearer(requiredResources);
        Assertions.assertEquals(150, loadBearer.getAvailableCapacityForResource(mockResourceType));

        int addedQuantity = loadBearer.tryAddToCart(mockResourceType, 50);
        Assertions.assertEquals(50, addedQuantity);
        Assertions.assertEquals(50, loadBearer.getQuantityForResource(mockResourceType)); // 假设有 getQuantityForResource 方法获取资源数量
        Assertions.assertEquals(100, loadBearer.getAvailableCapacityForResource(mockResourceType)); // 剩余容量减去已添加量

        // 测试添加超出容量的资源
        addedQuantity = loadBearer.tryAddToCart(mockResourceType, 150);
        Assertions.assertEquals(100, addedQuantity);
        Assertions.assertEquals(150, loadBearer.getQuantityForResource(mockResourceType));
        Assertions.assertEquals(0, loadBearer.getAvailableCapacityForResource(mockResourceType)); // 容量已满

        // 测试添加负数资源
        addedQuantity = loadBearer.tryAddToCart(mockResourceType, -10);
        Assertions.assertEquals(0, addedQuantity);
        Assertions.assertEquals(150, loadBearer.getQuantityForResource(mockResourceType)); // 由于数量不能为负，所以没有变化
        Assertions.assertEquals(0, loadBearer.getAvailableCapacityForResource(mockResourceType)); // 容量仍为 0

        // 测试添加 null 资源类型
        try {
            loadBearer.tryAddToCart(null, 50);
            Assertions.fail("Expected IllegalArgumentException");
        } catch (IllegalArgumentException e) {
            Assertions.assertEquals("Resource type cannot be null.", e.getMessage());
        }
    }

}
